#include<stdio.h>
#include<stdlib.h>
#include "inclusion.h"
#include "server.h"

/* Initialisation des tables */
void InitUserTable(UserTable * Users)
{
  Users->size = 0;
  Users->capacity = 1;
  Users->tab = malloc(sizeof(User));
}

int InitServerSocket(SOCKET * sock)
{
	SOCKADDR_IN sin;
	int sock_err, initialized; 
    *sock = socket(AF_INET, SOCK_STREAM, 0);
    
    if (*sock != INVALID_SOCKET)
    {
        printf("\nOuverture du socket serveur %d...OK !", *sock);
    	
    	sin.sin_addr.s_addr = htonl(INADDR_ANY);
    	sin.sin_family = AF_INET;
    	sin.sin_port = htons(PORT);
    	
    	sock_err = bind(*sock, (SOCKADDR*)&sin, sizeof(sin));
    	
    	if (sock_err != SOCKET_ERROR)
    	{
    	    printf("\nInitialisation du socket serveur %d...OK !", *sock);
    		initialized = 1;
    	}
    	else
    	{
    		printf("\nInitialisation du socket serveur %d...echec !", *sock);
    		initialized = 0;
    	}
    }
    else
    {
    	printf("\nOuverture du socket serveur %d...echec !", *sock);
    }
    
    return initialized;
}

void DetectConnect(UserTable * ptr_users, SOCKET sock)
{
	assert(sock != INVALID_SOCKET);
   	while (1)
   	{
   		/* Déclarations */
   		pthread_t threads[10];
   		User NewUser;
   		size_t len;
   		ThreadArg Arg;
   		SOCKET csock;
   		SOCKADDR_IN csin;
    	socklen_t recsize = sizeof(csin);
    		
    	/* On se met à l'écoute */
    	assert( listen(sock, 5) != SOCKET_ERROR );

		/* On attends qu'un client se connecte */
		csock = accept(sock, (SOCKADDR*)&csin, &recsize);
		assert( csock != INVALID_SOCKET );
   			
   		/* On reçoit la taille du pseudo du nouveau client */
   		assert( recv(csock, &len, sizeof(len), 0) != SOCKET_ERROR );
   			
   		/* On reçoit le pseudo du nouveau client */
   		char * pseudo = malloc(len*sizeof(char));
   		assert( recv(csock, pseudo, len, 0) != SOCKET_ERROR );
   		
   		/* On reçoit la structure User du nouveau client */
   		assert( recv(csock, &NewUser, sizeof(NewUser), 0) != SOCKET_ERROR );
			
		/* On set le pseudo et l'ID au nouveau client */
		SetUserName(&NewUser, pseudo);
		SetID(&NewUser, GenerateId(*ptr_users));
		SetSocket(&NewUser, csock);	
		
		/* On ajoute le nouveau client à la UserTable */	
		AddUser(ptr_users, NewUser);
		
		/* Préparation et exécution du thread pour le nouveau client */
		Arg.users = ptr_users;
		Arg.user = &NewUser;
		Arg.sock = &sock;
		pthread_create(&threads[GetID(NewUser)], NULL, Listen, (void*)&Arg);
	}
}

void * Listen(void * ptr_abstract)
{
	/* On récupère le pointeur sur la table d'utilisateurs */
	/* et le nouvel utilisateur à écouter				   */
	ThreadArg * Arg = (ThreadArg *)ptr_abstract;
	UserTable * ptr_users = Arg->users;
	User NewUser = *(Arg->user);
	
	SOCKET sock = *(Arg->sock);
	SOCKET csock = GetSocket(NewUser);
	
	assert (csock != INVALID_SOCKET );
	while(1)
	{
		size_t len;
		Message NewMessage;

		/* Reception de la chaîne envoyée par le client */
		assert( recv(csock, &len, sizeof(len), 0) != SOCKET_ERROR );
		char * string = (char*)malloc(len*sizeof(char));
   		assert( recv(csock, string, len, 0) != SOCKET_ERROR );
   		
   		/* On reçoit la structure message */
   		assert( recv(csock, &NewMessage, sizeof(Message), 0) != SOCKET_ERROR );
  		
  		/* Ajout de la chaîne et de l'User au message */
   		SetString(&NewMessage, string);
   		SetUser(&NewMessage, NewUser);

   		/* Si le client a envoyé "/exit", on déconnecte le client */
   		if (strcmp(GetString(NewMessage), "/exit") == 0)
   		{
   			shutdown(csock, 2);
   			closesocket(sock);
   			RemUser(ptr_users, GetID(NewUser));
   			printf("\nDeconnexion de %s...", GetUserName(NewUser));
   			fflush(stdout);
   			free(string);
   			pthread_exit(NULL);
   		}
   		else
   		{	
   			printf("\n%s : %s", GetUserName(NewUser), GetString(NewMessage));
   			fflush(stdout);
   			/* SendToAll(NewMessage, ptr_users); */
   			free(string);
   		}
   	}
}

void SendToAll(Message NewMessage, UserTable * Users)
{
	int i;
	
	/* Récupération de l'UserName et de la chaîne à envoyer */
	char * userName = GetUserName(GetUser(NewMessage));
	char * string = GetString(NewMessage);
	
	/* Détermination de leur taille pour l'envoi */
	size_t lenUserName = strlen(userName);
	size_t lenMessage = strlen(string);
	
	/* On compare les ID afin d'envoyer pseudo et message à */
	/* tous les clients de la UserTable, sauf à l'auteur du */
	/* message. 											*/
	for (i=0 ; i<Users->size ; i++)
	{
		if (Users->tab[i].id != GetID(GetUser(NewMessage)))
		{
			SOCKET csock = Users->tab[i].sock;

			/* Envoie pseudo */
			assert( send(csock, &lenUserName, sizeof(lenUserName), 0) != SOCKET_ERROR );
			assert( send(csock, userName, lenUserName, 0) != SOCKET_ERROR );
			/* Envoie Message */
			assert( send(csock, &lenMessage, sizeof(lenMessage), 0) != SOCKET_ERROR );
			assert( send(csock, string, lenMessage, 0) != SOCKET_ERROR );			
		}
	}  
}	

void AddUser(UserTable * Users, User newUser)
{
	int i;
	if (Users->capacity == Users->size)
	{
		Users->capacity *= 2;
		free(Users->tab);
		Users->tab = (User*)malloc((Users->capacity)*sizeof(User));
	}
    
    Users->tab[Users->size] = newUser;
    (Users->size)++;
}

int search(UserTable * Users, int ID)
{
   int trouve=0, i=0;
   while (!trouve && i!=Users->size)
   {
      if (Users->tab[i].id==ID)
      {
          trouve=1;
          return i;
      }
      else
      {
          i++;
      }
   }
   if (!trouve) return -1;
}

void RemUser(UserTable * Users, int ID)
{
    int i;
    int position = search(Users, ID);

    if (Users->size < (Users->capacity)/3)
    {
        Users->capacity = (Users->capacity)/2;
    }

    for (i=position ; i>=Users->size ; i++)
    {
        Users->tab[i] = Users->tab[i+1];
    }

    Users->size = (Users->size)-1;
}

int GenerateId(UserTable Users)
{
	int id, i;
	int existe=1; int trouve=0;
	
	srand(time(NULL));
	
	while(existe)
	{
		
		id=rand()%10;
		i=0;
		while(!trouve && i<Users.size)
		{
			if (id==Users.tab[i].id)
			{
				trouve=1;
			}
			else
			{
				i++;
			}
		}
		if (!trouve) existe=0;
	}
	return id;
}

void UserTableTestament(UserTable * Users)
{
    Users->size = 0;
    free(Users->tab);
    Users->capacity = 0;
}

void FreeServerSocket(SOCKET Sock, UserTable * Users)
{
	closesocket(Sock);
	UserTableTestament(Users);
}

void ServerTestRegression()
{
	printf("\n\n\nTestRegression du module Server :\n\n");

	/* Test de InitUserTable */
	UserTable Users;
	InitUserTable(&Users);
	if (Users.size == 0 && Users.capacity == 1 && sizeof(Users.tab) == sizeof(User))
	{
		printf("\nInitUserTable...OK !");
	}
	else
	{
		printf("\nInitUserTable...Erreur !");
	}
	
	/* Test de InitServerSocket */
	SOCKET Sock;
	int initialized = InitServerSocket(&Sock);
	if (initialized == 1)
	{
		printf("\nInitServerSocket...OK !");
	}
	else
	{
		printf("\nInitServerSocket...Erreur !");
	}
}
